﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Mighty.Common;
using Mighty.Brain;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.IO;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using log4net;
using log4net.Config;

/*
 * 노프랜드일 경우 0번 플레이어가 프랜드로 설정되는 것 같음
 */

namespace Mighty.PlayingBoard
{
    public class PlayingBoard
    {
        public IVisual iVisual { get; set; }
        public IDebug iDebug { get; set; }
        public GameMode gameMode = GameMode.Tester;

        public int timeOutRound = 60000;        // 60초 대기
        private int timeOutLimit = 1000000;    // 0이면 timeout하지 않음

        private Connector[] connectors = new Connector[5];
        private Card[] gameCards;
        private RecoveryBrain recoveryBrain = new RecoveryBrain();

        private Player[] players = new Player[5];

        private HandedCard[][] playersCards;
        private List<Betting> bettingHistory;
        private Betting betting;
        private AttackPlayer attackPlayer;
        private FriendPlayer friend;

        private Card jokerCall;
        private Card mighty;

        private Round[] roundHistory;
        private int roundIdx;
        private int roundCardIdx;

        private Score score;

        private Random random = new Random();
        public Network network = new Network();

        private static readonly ILog log = LogManager.GetLogger(typeof(PlayingBoard));

        public PlayingBoard()
        {
        }

        /// <summary>
        /// 게임내에서 사용하는 데이터를 초기화 한다
        ///  * gameinfo에 해당되는 데이터를 초기화 한다
        /// </summary>
        public void Init()
        {
            try
            {
                for (int i = 0; i < players.Length; i++)
                {
                    Player p = players[i];
                    if (p == null) throw new Exception("모든 플레이어가 설정되지 않았습니다");
                    p.pointCard = new List<RoundCard>();
                }

                gameCards = new Card[53];
                InitCard();

                playersCards = new HandedCard[5][];
                for (int i = 0; i < 5; i++)
                {
                    playersCards[i] = new HandedCard[13];
                    for (int j = 0; j < 13; j++)
                        playersCards[i][j] = new HandedCard();
                }

                bettingHistory = new List<Betting>();
                betting = new Betting();
                attackPlayer = new AttackPlayer();
                friend = new FriendPlayer();

                jokerCall = new Card();
                mighty = new Card();

                roundHistory = new Round[10];
                roundIdx = -1;

                score = new Score();
            }
            catch (Exception ex)
            {
                throw (ex);
            }
        }



        private void InitCard()
        {
            int cnt = 0;
            for (int i = 2; i <= 14; i++)
            {
                gameCards[cnt++] = new Card(Shape.Clover, i);
                gameCards[cnt++] = new Card(Shape.Diamond, i);
                gameCards[cnt++] = new Card(Shape.Spade, i);
                gameCards[cnt++] = new Card(Shape.Heart, i);
            }
            gameCards[cnt++] = new Card(Shape.Joker, 0);
        }

        /// <summary>
        /// 카드를 섞는다. 
        ///  * 섞어진 카드는 나누어 준다
        ///  * 만약 그림이 없는 사람이 있을 경우 다시 섞는다
        /// </summary>
        public void Shuffle()
        {
            // 카드섞기
            for (int i = 0; i < 10000; i++)
            {
                int k = random.Next(53);
                int l = random.Next(53);

                Card tmp = (Card)gameCards[k];
                gameCards[k] = gameCards[l];
                gameCards[l] = tmp;
            }

            // 카드나눠주기
            for (int i = 0; i < 50; i++)
            {
                int div = i / 10;
                int remain = i % 10;

                HandedCard hCard = new HandedCard(); ;
                hCard.shape = gameCards[i].shape;
                hCard.number = gameCards[i].number;

                playersCards[div][remain] = hCard;
            }

            // 그림이 한장도 없을때 다시 섞는다
            bool regame = false;
            for (int i = 0; i < 5; i++)
            {
                int cnt = 0;
                for (int j = 0; j < 10; j++) if (playersCards[i][j].number >= 10) cnt++;
                if (cnt == 0) regame = true;
            }
            if (regame) Shuffle();
        }

        private GameInfo GetLastestGameInfo(int position)
        {
            GameInfo g = new GameInfo();

            g.myInfo = players[position];
            g.players = players;

            g.myCards = playersCards[position];
            g.bettingHistory = bettingHistory;
            g.betting = betting;
            g.attackPlayer = attackPlayer;
            g.friend = friend;

            g.roundHistory = roundHistory;
            g.roundIdx = roundIdx;
            if (roundIdx > -1 && roundIdx < 10) g.round = g.roundHistory[roundIdx];
            else g.round = null;
            g.roundCardIdx = roundCardIdx;

            g.jokerCall = jokerCall;
            g.mighty = mighty;
            g.score = score;

            return g.Clone() as GameInfo;
        }

        private Object RequestEvent(Connector c, ActualStatus status)
        {
            Object requestResult = null;
            try
            {
                if (timeOutLimit > 0)
                {
                    requestResult = c.Request(status, timeOutLimit);
                }
                else
                {
                    requestResult = c.Request(status);
                }
            }
            catch (Exception ex)
            {
                DebugMsgExcetion(ex);
            }
            return requestResult;
        }

        private void RequestGameStart()
        {
            for (int i = 0; i < connectors.Length; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.GameStartEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }
        }

        private void RequestBettingReady()
        {
            for (int i = 0; i < connectors.Length; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.BettingReadyEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }
        }

        private bool RequestBettingTicEvent(int position)
        {

            bool[] pass = { false, false, false, false, false };
            bool isBetting = false;

            for (int i = position; i < 5; i++, i = i % 5)
            {
                int cnt = 0;
                for (int j = 0; j < 5; j++)
                {
                    if (pass[j]) cnt++;
                }
                if (cnt == 4 && isBetting) break;
                else if (cnt == 5) return false;

                if (pass[i]) continue;

                Betting b;
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    b = (Betting)RequestEvent(connectors[i], ActualStatus.BettingTicEvent);
                    CheckBettingTicEvent(b);
                }
                catch (ConnectorException ex)
                {
                    recoveryBrain.gameInfo = GetLastestGameInfo(i);
                    b = recoveryBrain.BettingTicEvent();
                    DebugMsgExcetion(ex);
                }

                bettingHistory.Add(b);
                if (b.isPass)
                {
                    pass[i] = true;
                    if (iDebug != null) iDebug.AddMessage(String.Format("player[{0}]: pass", i));
                }
                else
                {
                    isBetting = true;
                    b.position = i;
                    betting = b;
                    if (iDebug != null) iDebug.AddMessage(String.Format("player[{0}]: {1} {2}", i, b.limit, b.giruda));
                }


                for (int j = 0; j < 5; j++)
                {
                    try
                    {
                        connectors[j].SetGameInfo(GetLastestGameInfo(j));
                        RequestEvent(connectors[j], ActualStatus.BettingTalkEvent);
                    }
                    catch (ConnectorException ex)
                    {
                        DebugMsgExcetion(ex);
                    }
                }
            }
            return true;

        }

        private void CheckBettingTicEvent(Betting b)
        {
            if (b == null) throw new InvalidActionException(ActualStatus.BettingTicEvent, -1, "결과값으로 항상 betting 정보를 return해야 합니다", null);
            if (b.isPass) return;
            Betting lastBetting = null;
            foreach (Betting lb in bettingHistory)
            {
                if (lb.isPass == true) continue;
                lastBetting = lb;
            }
            if (lastBetting != null)
            {
                if (b.limit <= lastBetting.limit) throw new InvalidActionException(ActualStatus.BettingTicEvent, b.position, "이전보다 더 높은 LIMIT로 베팅해야 합니다", b);
            }
            if (b.limit < 13 || b.limit > 20) throw new InvalidActionException(ActualStatus.BettingTicEvent, b.position, "13-20사이의 LIMIT로 베팅해야 합니다", b);
        }

        private void RequestBettingCompleteEvent()
        {
            // attackPlayer 설정
            int position = betting.position;
            attackPlayer = new AttackPlayer();
            attackPlayer.SetPlayer(players[position]);

            // 추가3장의 카드 설정
            HandedCard[] tmpCards = playersCards[position];
            playersCards[position] = new HandedCard[13];
            for (int i = 0; i < 10; i++)
            {
                playersCards[position][i] = tmpCards[i];
            }
            for (int i = 0; i < 3; i++)
            {
                HandedCard hCard = new HandedCard(); ;
                hCard.shape = gameCards[50 + i].shape;
                hCard.number = gameCards[50 + i].number;
                hCard.cmdRemove = true;

                playersCards[position][10 + i] = hCard;
            }

            // 기루다무늬에 따른 mighty와 jokercall
            if (betting.giruda == Giruda.Clover) jokerCall = new Card(Shape.Spade, 3);
            else jokerCall = new Card(Shape.Clover, 3);
            if (betting.giruda == Giruda.Spade) mighty = new Card(Shape.Diamond, 14);
            else mighty = new Card(Shape.Spade, 14);

            // request
            for (int i = 0; i < connectors.Length; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.BettingCompleteEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }
        }

        private void RequestChoiceCardEvent()
        {


            HandedCard[] cards;
            try
            {
                connectors[attackPlayer.position].SetGameInfo(GetLastestGameInfo(attackPlayer.position));
                cards = (HandedCard[])RequestEvent(connectors[attackPlayer.position], ActualStatus.ChoiceCardEvent);
                CheckChoiceCardEvent(cards);
            }
            catch (ConnectorException ex)
            {
                recoveryBrain.gameInfo = GetLastestGameInfo(attackPlayer.position);
                cards = recoveryBrain.ChoiceCardEvent();
                DebugMsgExcetion(ex);
            }


            // 제거된 카드를 맨뒤로 보낸다. 10,11,12
            for (int cntRemove = 0; cntRemove < 3; cntRemove++)
            {
                for (int i = 12 - cntRemove; i >= 0; i--)
                {
                    if (cards[i].cmdRemove)
                    {
                        HandedCard tmp;
                        tmp = cards[i];
                        cards[i] = cards[12 - cntRemove];
                        cards[12 - cntRemove] = tmp;
                        break;
                    }
                }
            }
            playersCards[attackPlayer.position] = cards;
        }

        private void CheckChoiceCardEvent(HandedCard[] c)
        {
            if (c == null) throw new InvalidActionException(ActualStatus.ChoiceCardEvent, attackPlayer.position, "결과값으로 항상 HandedCard 정보를 return해야 합니다", null);

            int cntRemove = 0;
            for (int i = 0; i < 13; i++)
            {
                if (c[i].cmdRemove) cntRemove++;
            }

            if (cntRemove != 3) throw new InvalidActionException(ActualStatus.ChoiceCardEvent, attackPlayer.position, "버릴 3장의 카드를 선택해야 합니다", c);
        }


        private void RequestLastBettingEvent()
        {
            Betting b;
            try
            {
                connectors[attackPlayer.position].SetGameInfo(GetLastestGameInfo(attackPlayer.position));
                b = (Betting)RequestEvent(connectors[attackPlayer.position], ActualStatus.LastBettingEvent);
                CheckLastBettingEvent(b);
            }
            catch (ConnectorException ex)
            {
                recoveryBrain.gameInfo = GetLastestGameInfo(attackPlayer.position);
                b = recoveryBrain.LastBettingEvent();
                DebugMsgExcetion(ex);
            }

            if (b.giruda != betting.giruda)
            {
                b.position = attackPlayer.position;
                betting = b;
                bettingHistory.Add(b);

                if (betting.giruda == Giruda.Clover) jokerCall = new Card(Shape.Spade, 3);
                else jokerCall = new Card(Shape.Clover, 3);
                if (betting.giruda == Giruda.Spade) mighty = new Card(Shape.Diamond, 14);
                else mighty = new Card(Shape.Spade, 14);

                if (iDebug != null) iDebug.AddMessage(String.Format("player[{0}]: {1} {2}", attackPlayer.position, b.limit, b.giruda));
            }

            for (int i = 0; i < 5; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.LastBettingTalkEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }
        }

        private void CheckLastBettingEvent(Betting b)
        {
            if (b == null) throw new InvalidActionException(ActualStatus.LastBettingEvent, -1, "결과값으로 항상 betting 정보를 return해야 합니다", null);

            if (b.giruda != betting.giruda)
            {
                if (b.limit < betting.limit + 2) throw new InvalidActionException(ActualStatus.LastBettingEvent, attackPlayer.position, "기루다를 변경하였을 경우에는 LIMIT를 최소 +2해야 합니다", b);
            }
            if (b.limit < 13 || b.limit > 20) throw new InvalidActionException(ActualStatus.LastBettingEvent, attackPlayer.position, "13-20사이의 LIMIT로 베팅해야 합니다", b);
        }


        private void RequestChoiceFriendEvent()
        {

            FriendPlayer f;
            string strFriend = "";

            try
            {
                connectors[attackPlayer.position].SetGameInfo(GetLastestGameInfo(attackPlayer.position));
                f = (FriendPlayer)RequestEvent(connectors[attackPlayer.position], ActualStatus.ChoiceFriendEvent);
                CheckChoiceFriendEvent(f);
            }
            catch (ConnectorException ex)
            {
                recoveryBrain.gameInfo = GetLastestGameInfo(attackPlayer.position);
                f = recoveryBrain.ChoiceFriendEvent();
                DebugMsgExcetion(ex);
            }
            if (f.howToSelect == FriendPlayer.HowToSelect.player)
            {
                f.SetPlayer(f.playerCondition);
                f.founded = true;
            }
            friend = f;

            strFriend = f.GetFriendCondition(GetLastestGameInfo(attackPlayer.position));
            if (iDebug != null) iDebug.AddMessage(strFriend);

            for (int i = 0; i < 5; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.ChoiceFriendTalkEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }
        }

        private void CheckChoiceFriendEvent(FriendPlayer f)
        {
            if (f == null) throw new InvalidActionException(ActualStatus.ChoiceFriendEvent, attackPlayer.position, "결과값으로 항상 FriendPlayer 정보를 return해야 합니다", null);

            if (f.howToSelect == FriendPlayer.HowToSelect.card)
            {
                if (f.cardCondition == null) throw new InvalidActionException(ActualStatus.ChoiceFriendEvent, attackPlayer.position, "Card 프랜드를 선택할 경우에는 cardCondition가 NULL이면 안됩니다", f);
                if (f.cardCondition.shape != Shape.Joker)
                    if (f.cardCondition.number < 2 || f.cardCondition.number > 14) throw new InvalidActionException(ActualStatus.ChoiceFriendEvent, attackPlayer.position, "잘못된 cardCondition입니다", f);
            }
            else if (f.howToSelect == FriendPlayer.HowToSelect.player)
            {
                if (f.playerCondition == null) throw new InvalidActionException(ActualStatus.ChoiceFriendEvent, attackPlayer.position, "Card 프랜드를 선택할 경우에는 playerCondition이 NULL이면 안됩니다", f);
            }
        }


        private void RequestRoundReadyEvent()
        {
            for (int i = 0; i < connectors.Length; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.RoundReadyEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }
        }

        private void RequestRoundTicEvent()
        {
            //ShowTalkMsg("", 0);

            Round round = new Round();
            roundHistory[roundIdx] = round;
            //SetPlayerState(PlayerState.startRoundCard);

            // 전판에 이긴사람
            int previousWinnerPosition;
            if (roundIdx == 0) previousWinnerPosition = betting.position;
            else previousWinnerPosition = roundHistory[roundIdx - 1].winner;

            //InitAllThrowCard(); // 버리는 카드 위치 초기화

            for (roundCardIdx = 0; roundCardIdx < connectors.Length; roundCardIdx++)
            {

                int position = roundCardIdx + previousWinnerPosition;
                position = position % 5;

                // 사용자가 사용 할 수 있는 카드를 기록한다
                UpdatePlayerPossibleCard(position, round);

                HandedCard hc;
                RoundCard rc = new RoundCard();
                try
                {
                    connectors[position].SetGameInfo(GetLastestGameInfo(position));
                    hc = (HandedCard)RequestEvent(connectors[position], ActualStatus.RoundTicEvent);
                    if (hc == null) throw new InvalidActionException(ActualStatus.RoundTicEvent, position, "결과값으로 항상 HandedCard 정보를 return해야 합니다", null);

                    rc.shape = hc.shape;
                    rc.number = hc.number;
                    rc.owner = position;
                    rc.isJokerCall = hc.cmdJokerCall;
                    rc.jokerTarget = hc.cmdJokerTarget;
                    CheckRoundTicEvent(round, rc);
                }
                catch (ConnectorException ex)
                {
                    recoveryBrain.gameInfo = GetLastestGameInfo(position);
                    hc = (HandedCard)recoveryBrain.RoundTicEvent();
                    rc.shape = hc.shape;
                    rc.number = hc.number;
                    rc.owner = position;
                    rc.isJokerCall = hc.cmdJokerCall;
                    rc.jokerTarget = hc.cmdJokerTarget;
                    DebugMsgExcetion(ex);
                }

                long start = DateTime.Now.Ticks;
                if (iDebug != null) iDebug.AddMessage(String.Format("player[{0}]: {1} {2}", position, rc.shape, rc.number));
                long end = DateTime.Now.Ticks;
                if (log.IsDebugEnabled) log.Debug(string.Format("{0} player[{1}] {2} {3} ({4})", roundIdx, position, rc.shape, rc.number, (end - start) / 1000));

                // 프랜드를 찾는다
                if (friend.howToSelect == FriendPlayer.HowToSelect.card)
                {
                    if (rc.IsSame(friend.cardCondition))
                    {
                        friend.SetPlayer(players[position]);
                        friend.founded = true;
                    }
                }

                // 사용한 카드를 기록한다
                UpdatePlayerUsedCard(rc);
                round.cardStack[roundCardIdx] = rc;

                if (iVisual != null)
                {
                    if (rc.isJokerCall && roundCardIdx == 0) iVisual.Talk("조커콜!", position);
                    if (rc.shape == Shape.Joker && roundCardIdx == 0) iVisual.Talk(rc.jokerTarget.ToString() + "!", position);
                }

                for (int i = 0; i < 5; i++)
                {
                    try
                    {
                        connectors[i].SetGameInfo(GetLastestGameInfo(i));
                        RequestEvent(connectors[i], ActualStatus.RoundCardThrowEvent);
                    }
                    catch (ConnectorException ex)
                    {
                        DebugMsgExcetion(ex);
                    }
                }
            }
        }

        /// <summary>
        /// 매 라운드마다 각 플레이어의 카드에 대해서 사용할 수 있는 카드와 사용할 수 없는 카드를 구분한다
        ///  * position    : player위치
        ///  * round       : 해당 라운드에서 사용된 카드정보
        /// </summary>
        private void UpdatePlayerPossibleCard(int position, Round round)
        {
            // 사용한 카드를 제외하고는 모든 카드를 possible로 설정한다
            HandedCard[] cards = playersCards[position];
            for (int i = 0; i < 10; i++)
            {
                HandedCard card = cards[i];
                if (card.isUsed) card.isPossible = false;
                else card.isPossible = true;
            }

            // 첫번째카드
            if (roundCardIdx == 0)
            {

                // 첫번째 라운드에는 기루다 무늬를 not possible하게 한다
                if (roundIdx == 0)
                {
                    if (betting.giruda != Giruda.NoGiruda)
                    {
                        Shape g = (Shape)betting.giruda;
                        if (GetPlayerShapeCount(position, g) > 0)
                        {
                            for (int i = 0; i < 10; i++)
                            {
                                HandedCard card = cards[i];
                                if (!card.isPossible) continue;
                                if (card.shape == g) card.isPossible = false;
                            }
                        }
                    }
                }
            }

            // 첫번째카드가 아닐경우 
            if (roundCardIdx != 0)
            {
                Shape firstShape = round.cardStack[0].shape;
                // 조커라면 선택한 대상을 firstShape로 지정한다
                if (firstShape == Shape.Joker)
                {
                    firstShape = round.cardStack[0].jokerTarget;
                }

                // 첫번째 카드무늬와 동일한 무늬를 사용해야 한다
                if (GetPlayerShapeCount(position, firstShape) > 0)
                {
                    for (int i = 0; i < 10; i++)
                    {
                        HandedCard card = cards[i];
                        if (!card.isPossible) continue;
                        if (card.shape != firstShape) card.isPossible = false;
                    }
                }
            }

            // 특수카드는 아무때나 낼 수 있다.
            for (int i = 0; i < 10; i++)
            {
                HandedCard card = cards[i];
                if (card.isUsed) continue;

                if (card.shape == mighty.shape && card.number == mighty.number) card.isPossible = true;
                if (card.shape == Shape.Joker) card.isPossible = true;
            }

            // 조커를 사용할 수 있다는 전제 하에서
            if (roundCardIdx != 0)
            {
                // 조커콜
                RoundCard firstCard = round.cardStack[0];
                if (firstCard.shape == jokerCall.shape && firstCard.number == jokerCall.number)
                {
                    if (firstCard.isJokerCall)
                    {
                        if (GetPlayerShapeCount(position, Shape.Joker) == 1)
                        {
                            for (int i = 0; i < 10; i++)
                            {
                                HandedCard card = cards[i];
                                if (card.isUsed) continue;
                                if (card.shape != Shape.Joker) card.isPossible = false;
                            }
                        }
                    }
                }
            }
        }

        private void UpdatePlayerUsedCard(RoundCard rc)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard h = playersCards[rc.owner][i];
                if (rc.shape == h.shape && rc.number == h.number)
                {
                    h.isUsed = true;
                    break;
                }
            }
        }

        private void CheckRoundTicEvent(Round round, RoundCard rc)
        {

            int position = rc.owner;

            // 사용할 수 있는 카드인지 체크
            if (!IsPlayerCard(position, rc))
            {
                throw new InvalidActionException(ActualStatus.RoundTicEvent, position, "해당카드를 가지고 있지 않거나 이미 사용한 카드입니다", rc);
            }

            int idx = GetPlayerCardIndex(position, rc);
            if (idx == -1) throw new InvalidActionException(ActualStatus.RoundTicEvent, position, "해당카드를 가지고 있지 않습니다", rc);

            HandedCard c = playersCards[position][idx];
            if (!c.isPossible) throw new InvalidActionException(ActualStatus.RoundTicEvent, position, "사용 할 수 없는 카드를 사용했습니다", rc);
        }

        /// <summary>
        /// 두개의 카드가 동일한 shape와 number를 가졌는지 확인한다
        /// </summary>
        /// <param name="c1">card1</param>
        /// <param name="c2">card2</param>
        /// <returns></returns>
        private bool IsSameCard(Card c1, Card c2)
        {
            if (c1.shape == c2.shape && c1.number == c2.number) return true;
            if (c1.shape == Shape.Joker && c2.shape == Shape.Joker) return true;

            return false;
        }

        /// <summary>
        /// 해당 무늬가 기루다 무늬와 동일한지 확인한다
        /// </summary>
        /// <param name="s">shape</param>
        /// <returns></returns>
        private bool IsGiruda(Shape s)
        {
            Giruda g = betting.giruda;

            if (s == Shape.Clover && g == Giruda.Clover) return true;
            else if (s == Shape.Diamond && g == Giruda.Diamond) return true;
            else if (s == Shape.Heart && g == Giruda.Heart) return true;
            else if (s == Shape.Spade && g == Giruda.Spade) return true;
            return false;
        }

        /// <summary>
        /// 해당 카드가 마이티인지 확인한다
        /// </summary>
        /// <param name="c">card</param>
        /// <returns></returns>
        private bool IsMighty(Card c)
        {
            if (c.shape == mighty.shape && c.number == mighty.number) return true;
            return false;
        }

        /// <summary>
        /// 해당 카드가 조카콜인지 확인한다
        /// </summary>
        /// <param name="c">card</param>
        /// <returns></returns>
        private bool IsJokerCall(Card c)
        {
            if (c.shape == jokerCall.shape && c.number == jokerCall.number) return true;
            return false;
        }

        /// <summary>
        /// 플레이어가 해당카드를 가지고 있는지 확인한다
        /// </summary>
        /// <param name="p">player position</param>
        /// <param name="card">card</param>
        /// <returns></returns>
        private bool IsPlayerCard(int p, Card card)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard c = playersCards[p][i];
                if (IsSameCard(c, card)) return true;
            }
            return false;
        }

        /// <summary>
        /// 플레이어가 해당카드를 가지고 있다면 사용했는지 확인한다
        /// </summary>
        /// <param name="p">player position</param>
        /// <param name="card">card</param>
        /// <returns></returns>
        private bool IsUsedCard(int p, Card card)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard c = playersCards[p][i];
                if (IsSameCard(c, card))
                {
                    if (c.isUsed) return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 플레이어가 가지고 있는 카드의 위치를 찾아낸다
        /// </summary>
        /// <param name="p">player position</param>
        /// <param name="card">card</param>
        /// <returns></returns>
        private int GetPlayerCardIndex(int p, Card card)
        {
            for (int i = 0; i < 10; i++)
            {
                HandedCard c = playersCards[p][i];
                if (IsSameCard(c, card)) return i;
            }
            return -1;
        }

        /// <summary>
        /// 특정 플레이어가 가진 특정 무늬의 개수를 센다. 사용한 카드는 세지 않는다
        /// </summary>
        /// <param name="p">player position</param>
        /// <param name="s">shape</param>
        /// <returns></returns>
        private int GetPlayerShapeCount(int p, Shape s)
        {
            int count = 0;
            for (int i = 0; i < 10; i++)
            {
                HandedCard c = playersCards[p][i];
                if (c.isUsed) continue;

                if (c.shape == s) count++;
            }
            return count;
        }

        private void RequestRoundCompleteEvent()
        {
            Round r = roundHistory[roundIdx];
            RoundCard card = CheckRoundWinner(r);
            r.winner = card.owner;
            UpdateScoreCard(r);

            if (friend.howToSelect == FriendPlayer.HowToSelect.firstwin && roundIdx == 0)
            {
                friend.SetPlayer(players[r.winner]);
                friend.founded = true;
            }

            // debug
            if (iDebug != null) iDebug.AddMessage(String.Format("winner: player[{0}]", r.winner));

            for (int i = 0; i < connectors.Length; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.RoundCompleteEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }
        }

        /// <summary>
        /// 해당라운드의 윈카드를 찾아낸다
        /// </summary>
        /// <param name="round"></param>
        /// <returns></returns>
        private RoundCard CheckRoundWinner(Round round)
        {

            RoundCard winCard = null;
            RoundCard firstCard = round.cardStack[0];

            for (int i = 0; i < 5; i++)
            {
                RoundCard c = round.cardStack[i];

                // 마이티체크
                if (c.shape == mighty.shape && c.number == mighty.number)
                {
                    winCard = c;
                    break;
                }
            }
            if (winCard != null) return winCard;

            for (int i = 0; i < 5; i++)
            {
                RoundCard c = round.cardStack[i];
                // 조커체크
                if (c.shape == Shape.Joker)
                {
                    if (roundIdx > 0 && roundIdx < 9)
                    {
                        if (!(IsJokerCall(firstCard) && firstCard.isJokerCall))
                        {
                            winCard = c;
                            break;
                        }
                    }
                    else
                    {
                        if (i == 0) firstCard = round.cardStack[1];
                    }

                }
            }
            if (winCard != null) return winCard;


            // 기루다체크
            for (int i = 0; i < 5; i++)
            {
                RoundCard c = round.cardStack[i];
                if (IsGiruda(c.shape))
                {
                    if (winCard == null) winCard = c;
                    else if (c.number > winCard.number) winCard = c;
                }
            }
            if (winCard != null) return winCard;

            // 첫번째무늬체크
            for (int i = 0; i < 5; i++)
            {
                RoundCard c = round.cardStack[i];
                if (c.shape == firstCard.shape)
                {
                    if (winCard == null) winCard = c;
                    else if (c.number > winCard.number) winCard = c;
                }
            }
            if (winCard != null) return winCard;

            return firstCard;
        }

        /// <summary>
        /// 라운드가 끝났을때 승리자에게 점수카드를 추가한다
        /// </summary>
        /// <param name="round"></param>
        private void UpdateScoreCard(Round round)
        {
            for (int i = 0; i < 5; i++)
            {
                RoundCard c = round.cardStack[i];
                if (c.number >= 10)
                {
                    players[round.winner].pointCard.Add(c);
                }
            }
        }

        private void RequestGameEndEvent()
        {
            int dependPoint = 0;
            for (int i = 0; i < 5; i++)
            {
                Player player = players[i];
                if (player.position != attackPlayer.position && player.position != friend.position) dependPoint += player.pointCard.Count;
            }

            int attackPoint = 20 - dependPoint;
            int baseScore;
            int multi = 1;
            if (attackPoint >= betting.limit)
            {
                if (betting.limit == 20) multi *= 2;
                if (betting.giruda == Giruda.NoGiruda) multi *= 2;
                baseScore = ((betting.limit - 13) * 2 + (attackPoint - betting.limit)) * multi;
            }
            else
            {
                if (betting.limit <= dependPoint || attackPoint - betting.limit <= -7) multi *= 2;
                baseScore = (attackPoint - betting.limit) * multi;
            }

            score.cntDependerCard = dependPoint;
            score.baseScore = baseScore;
            if (attackPlayer.position == friend.position)   // 주공 == 프랜드
            {
                for (int i = 0; i < 5; i++)
                {
                    if (i == attackPlayer.position) score.playerScore[i] = baseScore * 4;
                    else score.playerScore[i] = baseScore * -1;
                }

            }
            else
            {
                for (int i = 0; i < 5; i++)
                {
                    if (i == attackPlayer.position) score.playerScore[i] = baseScore * 2;
                    else if (i == friend.position) score.playerScore[i] = baseScore * 1;
                    else score.playerScore[i] = baseScore * -1;
                }
            }
            for (int i = 0; i < 5; i++) players[i].score += score.playerScore[i];

            // request
            for (int i = 0; i < connectors.Length; i++)
            {
                try
                {
                    connectors[i].SetGameInfo(GetLastestGameInfo(i));
                    RequestEvent(connectors[i], ActualStatus.GameEndEvent);
                }
                catch (ConnectorException ex)
                {
                    DebugMsgExcetion(ex);
                }
            }

            if (iDebug != null)
            {
                iDebug.AddSummary(String.Format("조건: {0} {1}", betting.limit, betting.giruda));
                for (int i = 0; i < 5; i++)
                {
                    Player player = players[i];
                    String msg = String.Format("player[{0}]: {1} get", i, player.pointCard.Count);
                    if (player.position == attackPlayer.position) msg += " 주공";
                    if (player.position == friend.position) msg += " 프랜드";
                    if (iDebug != null) iDebug.AddSummary(msg);
                }
                iDebug.AddSummary(String.Format("depender card count : {0}", score.cntDependerCard));
                iDebug.AddSummary(String.Format("base score : {0}", score.baseScore));
            }
        }

        /// <summary>
        /// Player로써 AI를 설정한다
        /// </summary>
        /// <param name="name"></param>
        /// <param name="position"></param>
        /// <param name="t"></param>
        public void SetAI(String name, int position, Type t)
        {
            AIConnector ai;
            bool isAI = false;
            if (t != null)
            {
                ai = new AIConnector(t, position);
                isAI = true;
                connectors[position] = ai;

                ai.SetPlayingBoard(this);
            }

            Player p = new Player();
            p.isAI = isAI;
            p.name = name;
            p.position = position;

            players[position] = p;
        }

        /// <summary>
        /// Player로써 AI를 설정한다
        ///  * offline 모드일때 사용
        /// </summary>
        /// <param name="name"></param>
        /// <param name="position"></param>
        /// <param name="t"></param>
        /// <param name="nick"></param>
        /// <param name="score"></param>
        public void SetAI(String name, int position, Type t, String nick, int score)
        {
            SetAI(name, position, t);
            players[position].nick = nick;
            players[position].score = score;
        }

        /// <summary>
        /// Player로써 Mighty게임 유저를 설정한다
        /// </summary>
        /// <param name="name"></param>
        /// <param name="position"></param>
        public void SetPlayer(String name, int position)
        {

            PlayerConnector playerConnecter = new PlayerConnector(position);
            playerConnecter.playingBoard = this;

            Player p = new Player();
            p.isAI = false;
            p.name = name;
            connectors[position] = playerConnecter;
            p.position = position;

            players[position] = p;
        }

        /// <summary>
        /// Player로써 Mighty게임 유저를 설정한다
        ///  * offline 모드일때 사용
        /// </summary>
        /// <param name="name"></param>
        /// <param name="position"></param>
        /// <param name="nick"></param>
        /// <param name="score"></param>
        public void SetPlayer(String name, int position, String nick, int score)
        {
            SetPlayer(name, position);
            players[position].nick = nick;
            players[position].score = score;
        }
        
        public void SetClient(String name, String ip, int port, int position)
        {
            ClientConnector clientConnector = new ClientConnector(ip, port, position);
            clientConnector.SetPlayingBoard(this);

            Player p = new Player();
            p.isAI = false;
            p.name = name;
            p.ip = ip;
            p.port = port;
            p.position = position;
            connectors[position] = clientConnector;

            players[position] = p;
        }

        public void SetTimeOutLimit(int timeOut)
        {
            timeOutLimit = timeOut;
        }

        public int[] GetScore()
        {
            return score.playerScore;
        }

        private void sendGameInfo(GameInfo g, Socket client)
        {
            XmlSerializer xs = new XmlSerializer(typeof(GameInfo));
            StringBuilder sb = new StringBuilder();
            StringWriter sw = new StringWriter(sb);
            xs.Serialize(sw, g);
            sw.Close();

            byte[] data = Encoding.Default.GetBytes(sb.ToString());

            try
            {
                network.SendData(data, client);
            }
            catch (Exception ex)
            {
                if (iDebug != null) iDebug.AddMessage(ex.Message);
            }
        }

        protected void DebugMsgStage(String stage)
        {
            if (iDebug == null) return;

            String content;
            content = String.Format("- {0} -", stage);

            iDebug.AddMessage("");
            iDebug.AddMessage(content);
        }

        protected void DebugMsgExcetion(Exception ex)
        {
            if (iDebug == null) return;

            String content;
            content = " " + "관리되지않은에러";

            if (ex.Message != null) content += ": " + ex.Message;
            if (ex.InnerException != null) content += "\r\n(" + ex.InnerException + ")";
            iDebug.AddMessage(content);
        }

        protected void DebugMsgExcetion(ConnectorException ex)
        {
            if (iDebug == null) return;

            String content;
            content = " " + ex.GetType().Name;

            if (ex.whoscat != null) content += ": " + ex.whoscat;
            if (ex.footprint != null) content += "\r\n" + ex.footprint;
            iDebug.AddMessage(content);
        }

        public void StartGame()
        {
            if (iDebug != null)
            {
                iDebug.ClearMessage();
                iDebug.ClearSummary();
            }

            int position = 0;
            if (friend != null) position = friend.position; // 이전판의 프랜드부터

            Init();
            Shuffle();

            DebugMsgStage("GameStartEvent");
            RequestGameStart();

            DebugMsgStage("BettingReadyEvent");
            RequestBettingReady();

            DebugMsgStage("BettingTicEvent");
            if (!RequestBettingTicEvent(position))
            {
                StartGame();    
                return;
            }

            DebugMsgStage("BettingCompleteEvent");
            RequestBettingCompleteEvent();

            DebugMsgStage("ChoiceCardEvent");
            RequestChoiceCardEvent();

            DebugMsgStage("LastBettingEvent");
            RequestLastBettingEvent();

            DebugMsgStage("ChoiceFriendEvent");
            RequestChoiceFriendEvent();

            for (roundIdx = 0; roundIdx < 10; roundIdx++)
            {
                DebugMsgStage(String.Format("{0} round", roundIdx));

                RequestRoundReadyEvent();
                RequestRoundTicEvent();
                RequestRoundCompleteEvent();
            }

            DebugMsgStage("GameEndEvent");
            RequestGameEndEvent();
            GetLastestGameInfo(0);
        }

    }
}
